package io.github.uicdb.et.register;

import io.github.uicdb.et.ModAttributes;
import io.github.uicdb.et.ElementalTheory;
import io.github.uicdb.et.ModElements;
import io.github.uicdb.et.element.BasicElement;
import io.github.uicdb.et.ModEffects;
import io.github.uicdb.et.fluid.ElementStream;
import io.github.uicdb.et.selection.RandomSelection;
import lombok.Getter;
import net.minecraft.core.Holder;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.damagesource.DamageType;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectCategory;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.item.alchemy.Potion;
import net.minecraft.world.level.material.Fluid;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.attachment.AttachmentType;
import net.neoforged.neoforge.event.entity.EntityAttributeModificationEvent;
import net.neoforged.neoforge.fluids.BaseFlowingFluid;
import net.neoforged.neoforge.fluids.FluidType;
import net.neoforged.neoforge.registries.*;

import java.util.LinkedList;
import java.util.List;

public class RegisterHelper {
    @Getter
    final static List<ElementalObject> elementalObjects = new LinkedList<>();
    final static DeferredRegister<Attribute> ATTRIBUTES_DEFERRED_REGISTER = DeferredRegister.create(Registries.ATTRIBUTE, ElementalTheory.MODID);
    final static DeferredRegister<MobEffect> EFFECT_DEFERRED_REGISTER = DeferredRegister.create(Registries.MOB_EFFECT, ElementalTheory.MODID);
    final static DeferredRegister<BasicElement> ELEMENT_DEFERRED_REGISTER = DeferredRegister.create(ElementalTheory.BASIC_ELEMENT_REGISTRY, ElementalTheory.MODID);
    final static DeferredRegister<Potion> POTION_DEFERRED_REGISTER = DeferredRegister.create(Registries.POTION, ElementalTheory.MODID);
    final static DeferredRegister<RandomSelection> RANDOM_SELECTION_DEFERRED_REGISTER = DeferredRegister.create(ElementalTheory.RANDOM_SELECTION_REGISTRY, ElementalTheory.MODID);

    final static DeferredRegister.Items ITEM_DEFERRED_REGISTER = DeferredRegister.createItems(ElementalTheory.MODID);
    final static DeferredRegister.Blocks BLOCK_DEFERRED_REGISTER = DeferredRegister.createBlocks(ElementalTheory.MODID);
    private static final DeferredRegister<AttachmentType<?>> ATTACHMENT_TYPES = DeferredRegister.create(NeoForgeRegistries.Keys.ATTACHMENT_TYPES, ElementalTheory.MODID);
    private static final DeferredRegister<DataComponentType<?>> DATA_COMPONENT_TYPE_DEFERRED_REGISTER = DeferredRegister.create(BuiltInRegistries.DATA_COMPONENT_TYPE, ElementalTheory.MODID);
    private static final DeferredRegister<Fluid> FLUID_REGISTER = DeferredRegister.create(BuiltInRegistries.FLUID, ElementalTheory.MODID);
    private static final DeferredRegister<FluidType> FLUID_TYPES = DeferredRegister.create(NeoForgeRegistries.Keys.FLUID_TYPES, ElementalTheory.MODID);

    public static ElementalObject registerElemental(String key, int color) {
        DeferredHolder<BasicElement, BasicElement> element = ELEMENT_DEFERRED_REGISTER.register(key, BasicElement::new);

        Holder<Attribute> edb = ATTRIBUTES_DEFERRED_REGISTER.register(key + "_edb", () -> new ModAttributes.ElementAttribute("attribute.elemental_theory." + key + "_edb", 20, 0, Integer.MAX_VALUE));
        ResourceKey<DamageType> damageType = ResourceKey.create(Registries.DAMAGE_TYPE, ResourceLocation.fromNamespaceAndPath(ElementalTheory.MODID, key));

        var potionEDB = registerElementPotion(
                color, 50f, AttributeModifier.Operation.ADD_VALUE, edb, 20 * 90, 0
        );

        RANDOM_SELECTION_DEFERRED_REGISTER.register(key + "_edb", () -> new RandomSelection("edb", edb, 20, AttributeModifier.Operation.ADD_VALUE));
        RANDOM_SELECTION_DEFERRED_REGISTER.register(key + "_edb2", () -> new RandomSelection("edb2", edb, 0.12, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL));
        DeferredHolder<FluidType,FluidType> fluidType =FLUID_TYPES.register(key,()->new FluidType(FluidType.Properties.create()
                .descriptionId("block.elemental_theory."+key)
                .motionScale(1D)
                .canPushEntity(false)
                .canSwim(false)
                .canDrown(false)
                .fallDistanceModifier(1F)
                .pathType(null)
                .adjacentPathType(null)
                .density(0)
                .temperature(0)
                .viscosity(0))
        );
        DeferredHolder<Fluid,Fluid> sourceHolder = DeferredHolder.create(Registries.FLUID,ResourceLocation.fromNamespaceAndPath(ElementalTheory.MODID,key+"_source"));
        DeferredHolder<Fluid,Fluid> flowHolder = DeferredHolder.create(Registries.FLUID,ResourceLocation.fromNamespaceAndPath(ElementalTheory.MODID,key+"_flowing"));
        DeferredHolder<Fluid, ElementStream.Source> source = FLUID_REGISTER.register(key+"_source",()->new ElementStream.Source(new BaseFlowingFluid.Properties(fluidType,sourceHolder,flowHolder)));
        DeferredHolder<Fluid,ElementStream.Flowing> flowing = FLUID_REGISTER.register(key+"_flowing",()->new ElementStream.Flowing(new BaseFlowingFluid.Properties(fluidType,sourceHolder,flowHolder)));
        ElementalObject object = new ElementalObject(ResourceLocation.fromNamespaceAndPath(ElementalTheory.MODID, key), element, edb, potionEDB, damageType,fluidType,source,flowing);
        elementalObjects.add(object);
        return object;
    }

    private static Holder<Potion> registerElementPotion(int color, float amount, AttributeModifier.Operation operation, Holder<Attribute> attribute, int duration, int amplifier) {
        var resKey = attribute.getKey().location().getPath();
        DeferredHolder<MobEffect, MobEffect> mobEffect = EFFECT_DEFERRED_REGISTER.register(resKey, () -> new ModEffects.Effect(MobEffectCategory.NEUTRAL, color).addAttributeModifier(attribute,
                ResourceLocation.fromNamespaceAndPath(ElementalTheory.MODID, resKey),
                amount, operation
        ));
        return POTION_DEFERRED_REGISTER.register(resKey, () -> new Potion(new MobEffectInstance(mobEffect, duration, amplifier, true, true)));
    }

    private static boolean init = false;

    public static void init(IEventBus modBus) {
        if (init) return;
        ITEM_DEFERRED_REGISTER.register(modBus);
        BLOCK_DEFERRED_REGISTER.register(modBus);
        ELEMENT_DEFERRED_REGISTER.register(modBus);
        ATTRIBUTES_DEFERRED_REGISTER.register(modBus);
        EFFECT_DEFERRED_REGISTER.register(modBus);
        POTION_DEFERRED_REGISTER.register(modBus);
        ATTACHMENT_TYPES.register(modBus);
        DATA_COMPONENT_TYPE_DEFERRED_REGISTER.register(modBus);
        RANDOM_SELECTION_DEFERRED_REGISTER.register(modBus);
        FLUID_REGISTER.register(modBus);
        FLUID_TYPES.register(modBus);
        modBus.addListener(EntityAttributeModificationEvent.class, event -> {
            for (ElementalObject object : elementalObjects) {
                for (EntityType<? extends LivingEntity> entityType : event.getTypes()) {
                    event.add(entityType, object.getIncreasing());
                }
            }
        });
        init = true;
    }

    public static void end() {
        for (ElementalObject elementalObject : elementalObjects) {
            IElementalGetter.cast(elementalObject.increasing.value()).setElementalObject(elementalObject);
            IElementalGetter.cast(elementalObject.element.value()).setElementalObject(elementalObject);
            IElementalGetter.cast(elementalObject.fluidSource.value()).setElementalObject(elementalObject);
            IElementalGetter.cast(elementalObject.fluidFlowing.value()).setElementalObject(elementalObject);
        }
    }


}
